function ps2pdf(varargin)
%PS2PDF Function to convert a PostScript file to PDF using Ghostscript
%
%  Converts a postscript file into PDF. The resulting PDF file will contain
%  one page for each page defined in the postscript files, so a multi-page
%  postscript file, like those generated by using the '-append' option of 
%  MATLAB's print command, can be used to generate a multi-page PDF file. 
%
%   Ghostscript is a third-party application currently supplied with 
%   MATLAB. The caller may also specify a different version of Ghostscript
%   to use.
%
%   PS2PDF expects to be called with a set of parameter-value pairs. The
%   order of these is unimportant, but required parameters MUST be
%   specified
%
%   In the list below, required parameters are marked with an asterisk *
%        NOTE: ps2pdf can not use MATLAB's version of Ghostscript
%              in a deployed application; you MUST provide a 
%              the path to a separate instance of Ghostscript. This 
%              parameter is marked with a double asterisk **
%
%   Parameter:        Value:  
%   *  psfile         full or relative path to the postscript file to convert
%
%   *  pdffile        full or relative path to the pdf file to create
%
%   ** gscommand      path to Ghostscript executable to use; this will try
%                     to default to the version of Ghostscript shipped with 
%                     MATLAB, if any. If this value is specified you should 
%                     also specify the gsfontpath and gslibpath values.
%
%                     ** See note on deployed applications, above.
%
%      gsfontpath     full path to the Ghostscript font files
%                     If a gscommand is specified then this path should
%                     also be specified and reference the same Ghostscript
%                     version
%
%      gslibpath      full path to the Ghostscript library (.ps) files. 
%                     If a gscommand is specified then this path should
%                     also be specified and reference the same Ghostscript
%                     version
%
%                     If gscommand is NOT specified and we can determine
%                     the version of Ghostscript, if any, shipped with
%                     MATLAB, then this value will be overridden to use the
%                     path that references MATLAB's version of Ghostscript
%
%      gspapersize    paper size to use in the created .pdf file. If not 
%                     specified or the specified value is not recognized 
%                     it will use whatever default paper size is 
%                     built into the version of Ghostscript being run
%
%                         NOTE: no scaling of the input occurs - it's simply
%                         placed on a page with the specified paper size. 
%
%                         Valid values for gspapersize are: 
%                              'letter', 'ledger', 'legal', '11x17', 
%                              'archA', 'archB', 'archC', 'archD', 'archE', 
%                              'a0', 'a1', 'a2', 'a3','a4', 'a5',
%                              'a6', 'a7', 'a8', 'a9', 'a10'
%                         
%      deletepsfile   0 to keep the input ps file after creating pdf
%                     non-zero to delete the input ps file after creating pdf
%                     Default is 0: keep the input ps file (do NOT delete it)
%                        NOTE: if the pdf creation process fails, the input
%                        PS file will be kept regardless of this setting
%
%      verbose        0 to suppress display of status/progress info; 
%                     non-zero to allow display of status/progress info
%                     Default is 0 (no display)
%
% Example usage: 
%    use MATLAB's version of Ghostscript to generate an A4 pdf file
%      ps2pdf('psfile', 'input.ps', 'pdffile', 'output.pdf', 'gspapersize', 'a4')
%
%    use a local copy of Ghostcript to generate a file, and display some 
%    status/progress info while doing so.
%      ps2pdf('psfile', '../reports/input.ps', 'pdffile', 'c:\temp\output3.pdf', ...
%            'gspapersize', 'a4', 'verbose', 1, ...
%            'gscommand', 'C:\Program Files\GhostScript\bin\gswin32c.exe', ...
%            'gsfontpath', 'C:\Program Files\GhostScript\fonts', ...
%            'gslibpath', 'C:\Program Files\GhostScript\lib')
%
%    use MATLAB's version of Ghostscript to generate a pdf file and delete
%    the input.ps file when done 
%      ps2pdf('psfile', 'input.ps', 'pdffile', 'output.pdf', 'gspapersize', 'a4', 'deletepsfile', 1)

%   Update log: 
%      Jun 16, 2010: added check for deployed application
%      May 19, 2010: wrapped filenames sent to Ghostscript in quotes
%      May 06, 2010: updated how Ghostscript is found, don't rely on MATLAB version #
%      Aug 15, 2008: fixed bug where embedded space in location of 
%                    MATLAB's version of Ghostscript caused ps2pdf to fail.
%      Apr 16, 2008: added deletepsfile option

%   Copyright 2008-2010 The MathWorks, Inc.

   if nargin < 1 
      error('ps2pdf:parameters', 'No parameters specified. Type ''help ps2pdf'' for details on how to use this function.');
   end

   % parse input args
   gsData = LocalParseArgs(varargin{:});

   % setup the file that tells GS what we want it to do
   gsData = LocalCreateResponseFile(gsData); 

   gsDebug = 0;
   if gsData.verbose 
      fprintf('ps2pdf: input settings are:\n');
      if isfield(gsData, 'paperSizes')
          gsData = rmfield(gsData, 'paperSizes');
      end
      gsData  %#ok<NOPRT>
      fprintf('ps2pdf: response file for Ghostscript is:\n');
      type(gsData.responseFile);
      gsDebug = 1;
   end

   %to hold results/status from system call
   s = 0; %#ok<NASGU>
   r = ''; %#ok<NASGU>

   % run Ghostscript to convert the file
   if gsData.useBuiltin 
      [s, r] = gsData.cmd(['@' gsData.responseFile], gsData.psFile, gsDebug);
   else 
      [s, r] = system([gsData.cmd ' @"' gsData.responseFile '" "' gsData.psFile '"']);
   end

   if gsData.verbose
      disp( ['Ghostscript STDOUT: ' num2str(s) ] );
      disp( ['Ghostscript STDERR: ' r ] );
   else
      delete(gsData.responseFile)
   end

   if s && ~isempty(r)
      error('ps2pdf:ghostscript',  ['Problem converting PostScript. System returned error: ' num2str(s) '.' r]) 
   elseif s
      error('ps2pdf:ghostscript',  ['Problem calling GhostScript. System returned error: ' num2str(s)]) 
   end

   %if after all this we still couldn't create the file, report the error
   fid = fopen( gsData.pdfFile, 'r');
   if ( fid == -1 )
      error('ps2pdf:ghostscript', '%s', [ 'Ghostscript could not create ''' gsData.pfdFile '''.' ])
   else
      fclose( fid );
   end

   % if we get here, we successfully created pdf file; delete ps file if
   % requested to do so
   if gsData.deletePSFile 
       delete(gsData.psFile);
   end

end

%local function to parse arguments and fill in the gsData structure
%  .psFile - postscript file to convert
%  .pdfFile - pdf file to create
%  .cmd     - path/name of Ghostscript command to run or handle to gscript
%             builtin
%  .useBuiltin - 1 if using builtin gs command 
%  .fontPath - path to the Ghostscript fonts, if any
%  .libPath - path to the Ghostscript libs (Ghostscript .ps files), if any) 
%  .paperSize - paper size to set for resulting .pdf file 
%  .deletePSFile - 0 to keep (not delete) the input ps file if pdf created ok
%  .verbose - if non-zero, display some status/progress info to command window
function gsData = LocalParseArgs(varargin) 
    gsData.paperSizes = {'letter', 'ledger', 'legal', '11x17', 'archA', 'archB', ... 
                   'archC', 'archD', 'archE', 'a0', 'a1', 'a2', 'a3','a4', 'a5', ...
                   'a6', 'a7', 'a8', 'a9', 'a10'};

    %default values for some settings
    gsData.verbose      = 0; 
    gsData.useBuiltin   = 0;
    gsData.deletePSFile = 0; 
    
    for i = 1 : 2 : length(varargin)-1 
        param_arg = varargin{i};
        param_value = varargin{i+1};        
        switch(lower(param_arg)) 
            % path to ps file to conver
            case 'psfile'
               if ~exist(param_value, 'file')
                  error('print:ghostscript', ...
                      'Can not find postscript file <%s> to convert', ...
                      param_value)
               end
               gsData.psFile = param_value;

            % path to pdf file to create
            case 'pdffile'
                %verify we can create file at that location
                pdf_fid = fopen(param_value,'w');
                if pdf_fid < 0 
                    error('ps2pdf:invalidPDFFIle', ... 
                        'Can not open <%s> for writing', ...
                        param_value);
                end
                fclose(pdf_fid); 
                %delete temp file we created
                delete(param_value); 
                gsData.pdfFile = param_value;

            % full path to gs executable
            case 'gscommand' 
               if ~exist(param_value, 'file')
                  error('ps2pdf:ghostscriptCommand', ...
                      'Can not find Ghostscript executable (''gscommand'') <%s>',...
                      param_value)
               end
               if ispc && ~isempty(findstr(param_value, ' '))
                   param_value = ['"' param_value '"']; %#ok<AGROW>
               end
               gsData.cmd = param_value; 
                
            % full path to gs font dir
            case 'gsfontpath' 
               if ~exist(param_value, 'dir')
                   error('ps2pdf:ghostscriptFontPath', ...
                         'Can not find the directory <%s> for Ghostscript fonts (''gsfontpath'')', ...
                         param_value)
               end
               gsData.fontPath = param_value;

            % full path to gs lib dir
            case 'gslibpath' 
               if ~exist(param_value, 'dir')
                   error('ps2pdf:ghostscriptLibPath', ...
                         'Can not find the directory <%s> for Ghostscript library files (''gslibpath'')', ...
                         param_value)
               end
               gsData.libPath = param_value;
                
            % paper size 
            case 'gspapersize'
               idx = strcmpi(param_value, gsData.paperSizes);
               if ~any(idx)
                  warning('ps2pdf:papersize', ...
                        '''gspapersize'' value <%s> not found in the list of known sizes, ignoring it.', param_value);
               else
                  gsData.paperSize = gsData.paperSizes{idx};
               end

            % deletePSFile
            case 'deletepsfile'
               if isnumeric(param_value) 
                  gsData.deletePSFile = param_value; 
               else
                   warning('ps2pdf:deletepsfile', ...
                         '''deletepsfile'' value <%s> class <%s> should be numeric, defaulting to 0', ...
                         param_value, class(param_value));
               end
               
            % verbose
            case 'verbose'
               if isnumeric(param_value) 
                  gsData.verbose = param_value; 
               else
                   warning('ps2pdf:verbose', ...
                         '''verbose'' value <%s> class <%s> should be numeric, defaulting to 0', ...
                         param_value, class(param_value));
               end
               
            otherwise
               if isnumeric(param_value)
                   param_value = num2str(param_value);
               end
                  warning('ps2pdf:unknown', ...
                     'ignoring unknown parameter <%s> with value <%s>.', param_arg, param_value);
        end
    end    

    if ~isfield(gsData, 'psFile') 
        error('ps2pdf:noInputFile', ...
               'No input (psfile) file specified');
    end
    
    if ~isfield(gsData, 'pdfFile') 
        error('ps2pdf:noOutputFile', ...
               'No output (pdffile) file specified');
    end
    
    if ~isfield(gsData, 'cmd') 
        if isdeployed
            error('ps2pdf:deployedNeedsGhostscript', ...
                  'In order to use ''ps2pdf'' in a deployed application you must provide the path to a separate instance of Ghostscript.');
        end

        % updated code to find ghostscript - look for gs8x first, 
        % then try old location. Don't depend on MATLAB version #
        ghostDir = fullfile( matlabroot, 'sys', 'gs8x' );
        if ~exist(ghostDir, 'dir')
            [gsCmd, ghostDir] = Local_GetOldGhostscript();
            gsData.cmd = gsCmd;
        else
           gsData.cmd = Local_GetGscriptFcnHandle;
           if ~isempty(gsData.cmd)
              gsData.useBuiltin = 1; % use builtin Ghostscript
           end
        end
        if ~exist(ghostDir, 'dir')
           error('ps2pdf:ghostscriptCommand', ...
                 'Can not find Ghostscript installed with MATLAB in <%s>',...
                 ghostDir);
        end

        if ~isempty(gsData.cmd)
           % if using MATLAB's version of GhostScript, use same set of fonts and library files
           if isfield(gsData, 'fontPath') || isfield(gsData, 'libPath')
              warning('ps2pdf:ghostscriptPathOverride', ...
                    'Using MATLAB''s version of Ghostscript; overriding ''gsfontpath'' and ''gslibpath'' to use builtin MATLAB version');
           end
           gsData.fontPath = fullfile( ghostDir, 'fonts', '');
           gsData.libPath = fullfile( ghostDir, 'ps_files', '');
        else 
            error('ps2pdf:noGhostscriptCommand', ...
                  'Can not find Ghostscript program in MATLAB');
        end
    else
        % if gscommandpath was specified, 
        if ~isfield(gsData, 'fontPath') || ~isfield(gsData, 'libPath')
           warning('ps2pdf:ghostscriptCommandSuggestion', ...
                 ['When specifying a Ghostscript executable (''gscommand'') you should also '...
                 'specify both the ''gsfontpath'' and ''gslibpath'' locations']);
   
        end
    end
end

%local function to create the input file needed for Ghostscript
function gsData = LocalCreateResponseFile(gsData) 
   % open a response file to write out Ghostscript commands
   rsp_file = [tempname '.rsp'];
   rsp_fid = fopen (rsp_file, 'w');

   if (rsp_fid < 0)
      error('ps2pdf:responseFileCreate', 'Unable to create response file')
   end

   fprintf(rsp_fid, '-dBATCH -dNOPAUSE\n');
   if ~gsData.verbose
       fprintf(rsp_fid, '-q\n');
   end
   if isfield(gsData, 'libPath')
      fprintf(rsp_fid, '-I"%s"\n', gsData.libPath);
   end
   if isfield(gsData, 'fontPath')
      fprintf(rsp_fid, '-I"%s"\n', gsData.fontPath);
   end
   if isfield(gsData, 'paperSize') 
      fprintf( rsp_fid, '-sPAPERSIZE=%s\n', gsData.paperSize );
   end
   fprintf(rsp_fid, '-sOutputFile="%s"\n', gsData.pdfFile);
   fprintf(rsp_fid, '-sDEVICE=%s\n', 'pdfwrite');
   fclose(rsp_fid);
   gsData.responseFile = rsp_file;
end

%local function to get a handle to MATLAB's Ghostscript implementation
%NOTE: this may change or be removed in future releases
function gs = Local_GetGscriptFcnHandle()
  gs = '';
  p = which('-all', 'gscript');
  if ~isempty(p) 
      p = p{1};
      fpath = fileparts(p);
      olddir = cd(fpath);
      gs = @gscript;
      cd(olddir);
  end
end

% local function to try and get location of Ghostscript in older MATLAB
function [gsCmd, ghostDir] = Local_GetOldGhostscript
   ghostDir = fullfile( matlabroot, 'sys', 'ghostscript' );
   gsCmd = '';
   if ispc
      if exist(fullfile(ghostDir,'bin','win32','gs.exe'), 'file')
         gsCmd = fullfile(ghostDir,'bin','win32','gs.exe');
      end
   else 
      if exist(fullfile(ghostDir,'bin',lower(computer),'gs'), 'file')
         gsCmd = fullfile(ghostDir,'bin',lower(computer), 'gs');
      end
   end
   gsCmd = ['"' gsCmd '"'];
end
